package AFMAnalysis;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;

public class Edger {

	ArrayList<Edge> edges;
	Pixel[][] pixels;
	Pixel[][] scaledPixels;
	ArrayList<Pixel> startingPixels;
	ArrayList<ScaledBlob> scaledBlobArray;
	ArrayList<Integer> idKey;
	
	/**
	 * This is the constructor for this class.
	 * 
	 * @param pix The Pixel array for the image
	 */
	
	public Edger(Pixel pix[][]){
		
		pixels = pix;
		edges = new ArrayList<Edge>();
		scaledBlobArray = new ArrayList<ScaledBlob>();
		idKey = new ArrayList<Integer>();
	}
	
	/**
	 * This method creates the edges for an image by first scaling the image, then
	 * adding a new edge for each parallel blob, and finally solving the edge for 
	 * of the starting pixels
	 * 
	 * @param blobArray The blobArray that will have it's edges solved
	 */
	
	public void createEdges(ArrayList<Blob> blobArray){
		
		//Scale up the blobArray
		scaleUp(blobArray);
		int counter = 0;
		
		//Add a new edge to the edge ArrayList for each parallel blob
		for(int count = 0; count < startingPixels.size(); count++){
			
			if(startingPixels.get(count).isParallel){
				
				edges.add(new Edge(startingPixels.get(count).pBlobID));
			}
		}

		System.out.println(edges.size());

		//Solve the edge of each parallel blob using the solveEdge2 method
		for(int count = 0; count < startingPixels.size(); count++){

			if(startingPixels.get(count).isParallel){
				solveEdge2(startingPixels.get(count), edges.get(counter));
				counter++;
			}
		}
	}
	
	/*public void solveEdge(Pixel start, Edge edge){
		
		boolean solving = true;
		edge.pixels.add(start);
		Pixel curr = start;
		ArrayList<Pixel> temp = new ArrayList<Pixel>();
		int counter = 0, counter1 = 0;
		
		while(solving){
			
			if(pixels[curr.x][curr.y-1].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x][curr.y-1])){

				temp.add(pixels[curr.x][curr.y-1]);
			}
			if(pixels[curr.x+1][curr.y].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x+1][curr.y])){

				temp.add(pixels[curr.x+1][curr.y]);
			}
			if(pixels[curr.x][curr.y+1].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x][curr.y+1])){

				temp.add(pixels[curr.x][curr.y+1]);
			}
			if(pixels[curr.x-1][curr.y].pBlobID == start.pBlobID &&  
					isOtherAround(pixels[curr.x-1][curr.y])){

				temp.add(pixels[curr.x-1][curr.y]);
			}
			if(pixels[curr.x-1][curr.y-1].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x-1][curr.y-1])){

				temp.add(pixels[curr.x-1][curr.y-1]);
			}
			if(pixels[curr.x+1][curr.y-1].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x+1][curr.y-1])){

				temp.add(pixels[curr.x+1][curr.y-1]);
			}
			if(pixels[curr.x+1][curr.y+1].pBlobID == start.pBlobID &&  
					isOtherAround(pixels[curr.x+1][curr.y+1])){

				temp.add(pixels[curr.x+1][curr.y+1]);
			}
			if(pixels[curr.x-1][curr.y+1].pBlobID == start.pBlobID && 
					isOtherAround(pixels[curr.x-1][curr.y+1])){

				temp.add(pixels[curr.x-1][curr.y+1]);
			}
			
			if(start.pBlobID == 49 && counter1 < 200){
				System.out.println(counter1);
				System.out.println(counter);
				System.out.println(temp.size());
				System.out.println(curr.x + ", " + curr.y);
			}
			
			if(temp.size() == 1){

				curr = temp.get(0);
			}
			
			else if(temp.size() == 2){
				
				if(temp.get(0) == edge.pixels.get(edge.pixels.size()-1)){
					
					if(!edge.pixels.contains(temp.get(1))){
						
						edge.pixels.add(temp.get(1));
						curr = temp.get(1);
					}
					else if(edge.pixels.contains(temp.get(1)) && temp.get(1) != start){
						
						curr = temp.get(1);
					}
					else{
						
						solving = false;
					}
				}
						
				else if(temp.get(1) == edge.pixels.get(edge.pixels.size()-1)){
					
					if(!edge.pixels.contains(temp.get(0))){
						
						edge.pixels.add(temp.get(0));
						curr = temp.get(0);
					}
					else if(edge.pixels.contains(temp.get(0)) && temp.get(0) != start){
						
						curr = temp.get(0);
					}
					else{
						
						solving = false;
					}
				}
				else{

					if(edge.pixels.contains(temp.get(0)) && edge.pixels.contains(temp.get(1))){
						
						solving = false;
					}
					
					else{
						
						for(int count = 0; count < temp.size(); count++){

							if(!edge.pixels.contains(temp.get(count))){

								edge.pixels.add(temp.get(count));
								curr = temp.get(count);
								count = temp.size();
							}
						}
					}
				}
			}
			
			else{
				
				counter = 0;
				
				for(int count = 0; count < temp.size(); count++){

					if(!edge.pixels.contains(temp.get(count))){

						counter++;
					}
				}
				
				if(counter == temp.size()){
					
					for(int count = 0; count < temp.size(); count++){
						
						if(temp.get(count) != edge.pixels.get(edge.pixels.size()-1) || temp.get(count) != edge.pixels.get(edge.pixels.size()-2) || temp.get(count) == start){
							
							solving = false;
						}
						else{

							curr = temp.get(count);
							count = temp.size();
						}
					}
				}
				else if(counter == 0){
					
					for(int count = 0; count < temp.size(); count++){

						if(temp.get(count) == start){

							solving = false;
						}
					}
					
					if(solving){

						if(pixels[curr.x-1][curr.y].pBlobID == start.pBlobID &&  
								isOtherAround(pixels[curr.x-1][curr.y])){

							curr = pixels[curr.x-1][curr.y];
						}
						else if(pixels[curr.x][curr.y-1].pBlobID == start.pBlobID && 
								isOtherAround(pixels[curr.x][curr.y-1])){

							curr = pixels[curr.x][curr.y-1];
						}
						else if(pixels[curr.x+1][curr.y].pBlobID == start.pBlobID && 
								isOtherAround(pixels[curr.x+1][curr.y])){

							curr = pixels[curr.x+1][curr.y];
						}
						else if(pixels[curr.x][curr.y+1].pBlobID == start.pBlobID && 
								isOtherAround(pixels[curr.x][curr.y+1])){

							curr = pixels[curr.x][curr.y+1];
						}
					}
				}
				else{
					
					for(int count = 0; count < temp.size(); count++){

						if(!edge.pixels.contains(temp.get(count))){

							edge.pixels.add(temp.get(count));
							curr = temp.get(count);
							count = temp.size();
						}
					}
				}
				
			}
			
			temp.clear();
			counter1++;
		}
	}*/
	
	/*public boolean isOtherAround(Pixel pixel){
		
		if(pixels[pixel.x-1][pixel.y-1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x][pixel.y-1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x+1][pixel.y-1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x+1][pixel.y].pBlobID != pixel.pBlobID ||
				pixels[pixel.x+1][pixel.y+1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x][pixel.y+1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x-1][pixel.y+1].pBlobID != pixel.pBlobID ||
				pixels[pixel.x-1][pixel.y].pBlobID != pixel.pBlobID){
			
			return true;
		}
		return false;
	}*/

	/**
	 * This method looks around a given Pixel to see if there are any other Pixels
	 * with the same blob ID as the input Pixel. It returns true if there IS AT LEAST
	 * ONE Pixel with a different ID.
	 * 
	 * @param pixel The Pixel to look around
	 * @return True if there are different Pixels around
	 */
	
	public boolean isOtherAround(Pixel pixel){

		if(scaledPixels[pixel.x-1][pixel.y-1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x][pixel.y-1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x+1][pixel.y-1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x+1][pixel.y].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x+1][pixel.y+1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x][pixel.y+1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x-1][pixel.y+1].pBlobID != pixel.pBlobID ||
				scaledPixels[pixel.x-1][pixel.y].pBlobID != pixel.pBlobID){

			return true;
		}
		return false;
	}

	/**
	 * This method simply draws the edges that have been created for the scaled image
	 * in red.
	 * 
	 * @param location The location of the output file
	 * @param name The name of the output file
	 */
	
	public void drawEdges(File location, String name){

		BufferedImage newImage = new BufferedImage(this.scaledPixels.length, this.scaledPixels.length, BufferedImage.TYPE_INT_RGB);

		for(int count = 0; count < edges.size(); count++){
			for(int count1 = 0; count1 < edges.get(count).pixels.size(); count1++){
				
				newImage.setRGB(edges.get(count).pixels.get(count1).x, edges.get(count).pixels.get(count1).y, Color.RED.getRGB());
			}
		}
		try {
			File rendered = new File(location.getAbsolutePath(), name + "Edges.png");
			ImageIO.write(newImage, "png", rendered);
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	/**
	 * This method solves for the edge locations around a Blob by starting at an edge
	 * Pixel and continuing forward until the entire edge is mapped in the following
	 * way:
	 * [5][1][6]
	 * [4][*][2]
	 * [8][3][7]
	 * 
	 * @param start The starting Pixel (must be on an edge to begin)
	 * @param edge The Edge to be created.
	 */
	
	public void solveEdge2(Pixel start, Edge edge){

		boolean solving = true;
		edge.pixels.add(start);
		start.isEdge = true;
		Pixel curr = start;

		while(solving){

			//1
			if(scaledPixels[curr.x][curr.y-1].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x][curr.y-1]) &&
					isOtherAround(scaledPixels[curr.x][curr.y-1])){

				edge.pixels.add(scaledPixels[curr.x][curr.y-1]);
				curr = scaledPixels[curr.x][curr.y-1];
			}
			//2
			else if(scaledPixels[curr.x+1][curr.y].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x+1][curr.y]) &&
					isOtherAround(scaledPixels[curr.x+1][curr.y])){

				edge.pixels.add(scaledPixels[curr.x+1][curr.y]);
				curr = scaledPixels[curr.x+1][curr.y];
			}
			//3
			else if(scaledPixels[curr.x][curr.y+1].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x][curr.y+1]) &&
					isOtherAround(scaledPixels[curr.x][curr.y+1])){

				edge.pixels.add(scaledPixels[curr.x][curr.y+1]);
				curr = scaledPixels[curr.x][curr.y+1];
			}
			//4
			else if(scaledPixels[curr.x-1][curr.y].pBlobID == start.pBlobID &&  
					!edge.pixels.contains(scaledPixels[curr.x-1][curr.y]) &&
					isOtherAround(scaledPixels[curr.x-1][curr.y])){

				edge.pixels.add(scaledPixels[curr.x-1][curr.y]);
				curr = scaledPixels[curr.x-1][curr.y];
			}
			//5
			else if(scaledPixels[curr.x-1][curr.y-1].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x-1][curr.y-1]) &&
					isOtherAround(scaledPixels[curr.x-1][curr.y-1])){

				edge.pixels.add(scaledPixels[curr.x-1][curr.y-1]);
				curr = scaledPixels[curr.x-1][curr.y-1];
			}
			//6
			else if(scaledPixels[curr.x+1][curr.y-1].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x+1][curr.y-1]) &&
					isOtherAround(scaledPixels[curr.x+1][curr.y-1])){

				edge.pixels.add(scaledPixels[curr.x+1][curr.y-1]);
				curr = scaledPixels[curr.x+1][curr.y-1];
			}
			//7
			else if(scaledPixels[curr.x+1][curr.y+1].pBlobID == start.pBlobID &&  
					!edge.pixels.contains(scaledPixels[curr.x+1][curr.y+1]) &&
					isOtherAround(scaledPixels[curr.x+1][curr.y+1])){

				edge.pixels.add(scaledPixels[curr.x+1][curr.y+1]);
				curr = scaledPixels[curr.x+1][curr.y+1];
			}
			//8
			else if(scaledPixels[curr.x-1][curr.y+1].pBlobID == start.pBlobID && 
					!edge.pixels.contains(scaledPixels[curr.x-1][curr.y+1]) &&
					isOtherAround(scaledPixels[curr.x-1][curr.y+1])){

				edge.pixels.add(scaledPixels[curr.x-1][curr.y+1]);
				curr = scaledPixels[curr.x-1][curr.y+1];
			}
			else{
				
				solving = false;
			}
			curr.isEdge = true;
		}
	}
	
	/**
	 * This method scales the image by a factor of three to create a larger image to 
	 * process for edging/grain boundaries.
	 * 
	 * @param blobArray The blobArray to be scaled up
	 */
	
	public void scaleUp(ArrayList<Blob> blobArray){
		
		scaledPixels = new Pixel[pixels.length*3][pixels.length*3];
		startingPixels = new ArrayList<Pixel>();
		
		//Scale up all the Pixels from the original Pixels
		for(int count1 = 0; count1 < scaledPixels.length; count1++){
			for(int count = 0; count < scaledPixels.length; count++){
				
				scaledPixels[count][count1] = new Pixel(pixels[count/3][count1/3].isPerpendicular,
						pixels[count/3][count1/3].isParallel,
						pixels[count/3][count1/3].pBlobID,
						pixels[count/3][count1/3].color,
						null, count, count1);
			}
		}
		
		//Put all of the starting pixels from the blobArray into the startingPixels
		for(int count = 0; count < blobArray.size(); count++){
			
			startingPixels.add(scaledPixels[blobArray.get(count).head.x*3][blobArray.get(count).head.y*3]);
		}
		
		//Iterate through the large (scaled) image
		for(int count1 = 0; count1 < scaledPixels.length; count1++){
			for(int count = 0; count < scaledPixels.length; count++){
				
				//If it's not a black space
				if(scaledPixels[count][count1].pBlobID != -1){
					//If the idKey contains the current Pixel's blob id
					if(idKey.contains(scaledPixels[count][count1].pBlobID)){
						//Add that pixel to the corresponding scaledBlob
						scaledBlobArray.get(idKey.indexOf(scaledPixels[count][count1].pBlobID)).pixels.add(scaledPixels[count][count1]);
					}
					else{
						//If it's not in the idKey yet, add it and make a new ScaledBlob with
						//that Pixel as the first element
						idKey.add(scaledPixels[count][count1].pBlobID);
						scaledBlobArray.add(new ScaledBlob());
						scaledBlobArray.get(scaledBlobArray.size()-1).pixels.add(scaledPixels[count][count1]);
					}
				}
			}
		}
	}
}
